/*
    a04_thread -
    展示了g_thread, g_mutex和g_cond等基本用法。
*/

#include <glib.h>

const int ThreadNum = 10;

static gpointer thread1_func(gpointer data)
{
    int num = GPOINTER_TO_INT(data);
    g_print("thread%d start...\n", num);

    g_print("thread data: thread %d\n", num);

    g_usleep(1000000);

    g_print("thread%d over.\n", num);
    return NULL;
}

void  show_basic_thread(void)
{
    GThread *thrd[ThreadNum];

    for(int i = 0; i<ThreadNum; i++)
    {
        g_print("start thread %d\n", i);
        thrd[i] = g_thread_new("threadName", thread1_func, GINT_TO_POINTER(i));
    }
    
    for(int i = 0; i<ThreadNum; i++)
    {
        g_thread_join(thrd[i]); //g_thread_join()内含g_thread_unref()
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

static GMutex mutex;        //mutex用于共享资源的访问
static int value;

void inc_value(int i)
{
    value++;
    g_print("thread %d inc value to %d\n", i, value);
}

void dec_value(int i)
{
    value--;
    g_print("thread %d dec value to %d\n", i, value);
}

static gpointer add_thread(gpointer data)
{
    int num = GPOINTER_TO_INT(data);

    g_mutex_lock(&mutex);
    inc_value(num);
    g_mutex_unlock(&mutex);

    g_thread_yield();

    g_mutex_lock(&mutex);
    inc_value(num);
    g_mutex_unlock(&mutex);

    return NULL;
}

static gpointer dec_thread(gpointer data)
{
    int num = GPOINTER_TO_INT(data);

    g_mutex_lock(&mutex);
    dec_value(num);
    g_mutex_unlock(&mutex);

    g_thread_yield();

    g_mutex_lock(&mutex);
    dec_value(num);
    g_mutex_unlock(&mutex);

    return NULL;
}


void show_mutex(void)
{
    GThread *thrd[ThreadNum];

    g_mutex_init(&mutex);
    g_mutex_lock(&mutex);    

    for(int i = 0; i<ThreadNum; i++)
    {
        g_print("start thread %d\n", i);
        thrd[i] = g_thread_new("threadName", (i%2 == 0) ? add_thread : dec_thread, GINT_TO_POINTER(i));
    }
    
    g_mutex_unlock(&mutex);    

    for(int i = 0; i<ThreadNum; i++)
    {
        g_thread_join(thrd[i]); //g_thread_join()内含g_thread_unref()
    }

    g_mutex_clear(&mutex);
}

/////////////////////////////////////////////////////////////////////////////////////////

static GCond cond;     //cond要与mutex联合使用, 用于多线程的同步
static gboolean is_signal = FALSE;
static gint cond_value = 0;

void push_data(int threadNum, int times)
{
    g_mutex_lock(&mutex);
    is_signal = TRUE;
    g_cond_signal(&cond);

    //do action
    cond_value++;
    g_print("thread %d push_data %d \n", threadNum, times);

    g_mutex_unlock(&mutex);
}

void pop_data(int data)
{
    g_mutex_lock(&mutex);
    while(!is_signal)               //cond必须使用while(loop){...g_cond_wait();}, 因为g_cond_wait()可能虚假唤醒而返回
    {
        g_print("thread %d enter into g_cond_wait\n", data);
        g_cond_wait(&cond, &mutex);
        g_print("thread %d leave from g_cond_wait\n", data);
    }
    is_signal = FALSE;

    //do action
    cond_value--;
    g_print("thread %d pop_data %d \n", data, cond_value);

    g_mutex_unlock(&mutex);
}


static gpointer thread_push(gpointer data)
{
    int num = GPOINTER_TO_INT(data);
    g_print("thread %d push.....\n", num);

    for(int i = 0; i< ThreadNum -1; i++)
        push_data(num, i);
    
    return NULL;
}

static gpointer thread_pop(gpointer data)
{
    int num = GPOINTER_TO_INT(data);
    g_print("thread %d pop.....\n", num);

    pop_data(num);
    
    return NULL;
}

void show_cond(void)
{
    GThread *thrd[ThreadNum];

    g_mutex_init(&mutex);
    g_cond_init(&cond);

    for(int i = 0; i< ThreadNum-1; i++)
    {
        thrd[i] = g_thread_new("threadPop", thread_pop, GINT_TO_POINTER(i));
    }

    thrd[ThreadNum - 1] = g_thread_new("threadPush", thread_push, GINT_TO_POINTER(ThreadNum - 1));

    g_cond_clear(&cond);
    g_mutex_clear(&mutex);

    
    for(int i = 0; i<ThreadNum; i++)
    {
        g_thread_join(thrd[i]); //g_thread_join()内含g_thread_unref()
    }

}


static gpointer thread_push_broadcast(gpointer data)
{
    int num = GPOINTER_TO_INT(data);
    g_print("thread %d push broadcast.....\n", num);

    g_mutex_lock(&mutex);
    is_signal = TRUE;
    g_cond_broadcast(&cond);

    //do action
    cond_value += ThreadNum - 1;
    g_print("thread %d push_data %d \n", num, cond_value);

    g_mutex_unlock(&mutex);
    
    return NULL;
}

static gpointer thread_pop_broadcast(gpointer data)
{
    int num = GPOINTER_TO_INT(data);
    g_print("thread %d push broadcast.....\n", num);

    g_mutex_lock(&mutex);
    while(!is_signal)
    {
        g_print("thread %d enter into g_cond_wait\n", num);
        g_cond_wait(&cond, &mutex);
        g_print("thread %d leave from g_cond_wait\n", num);
    }
    //is_signal = FALSE;        //与thread_pop()不同之处

    //do action
    cond_value--;
    g_print("thread %d pop_data %d \n", num, cond_value);

    g_mutex_unlock(&mutex);
    
    return NULL;
}

static void show_cond_broadcast(void)
{
    GThread *thrd[ThreadNum];

    g_mutex_init(&mutex);
    g_cond_init(&cond);

    for(int i = 0; i< ThreadNum-1; i++)
    {
        thrd[i] = g_thread_new("threadPop", thread_pop_broadcast, GINT_TO_POINTER(i));
    }

    thrd[ThreadNum - 1] = g_thread_new("threadPush", thread_push_broadcast, GINT_TO_POINTER(ThreadNum - 1));

    for(int i = 0; i<ThreadNum; i++)
    {
        g_thread_join(thrd[i]); //g_thread_join()内含g_thread_unref()
    }

    g_cond_clear(&cond);
    g_mutex_clear(&mutex);
}

int main(int argc, char* argv[])
{
    g_print("a04_thread sample.\n");

    //show_basic_thread();
    //show_mutex();
    //show_cond();
    show_cond_broadcast();


    return 0;
}